package io.vertx.guides.wiki.database

import io.vertx.core.AsyncResult
import io.vertx.core.Future
import io.vertx.core.Handler
import io.vertx.core.json.JsonArray
import io.vertx.core.json.JsonObject
import io.vertx.core.logging.LoggerFactory
import io.vertx.ext.jdbc.JDBCClient
import java.util.stream.Collectors


/**
 * Created by zhangwei on 2017/7/19.
 */
class WikiDatabaseServiceImpl(val dbClient: JDBCClient,
                              val sqlQueries: MutableMap<SqlQuery, String>,
                              readyHandler: Handler<AsyncResult<WikiDatabaseService>>) : WikiDatabaseService {

    companion object {
       private val LOGGER = LoggerFactory.getLogger(WikiDatabaseServiceImpl::class.java)
    }



    init {
        dbClient.getConnection { ar ->
            if (ar.failed()) {
                LOGGER.error("Could not open a database connection", ar.cause());
                readyHandler.handle(Future.failedFuture(ar.cause()));
            }else {
                val connection = ar.result()
                connection.execute(sqlQueries[SqlQuery.CREATE_PAGES_TABLE]) { create ->
                    connection.close()
                    if (create.failed()) {
                        LOGGER.error("Database preparation error", create.cause());
                        readyHandler.handle(Future.failedFuture(create.cause()));
                    } else {
                        readyHandler.handle(Future.succeededFuture(this));
                    }
                }
            }
        }
    }



    override fun fetchAllPages(resultHandler: Handler<AsyncResult<JsonArray>>): WikiDatabaseService {
        dbClient.getConnection { car ->
            if (car.succeeded()) {
                val connection = car.result()
                connection.query(sqlQueries[SqlQuery.ALL_PAGES]) { res ->
                    connection.close()
                    if (res.succeeded()) {
                        val pages = JsonArray((res.result()
                                .results
                                .stream()
                                .map { json -> json.getString(0) }
                                .sorted()
                                .collect(Collectors.toList())) as List<String>)

                        resultHandler.handle(Future.succeededFuture(pages))
                    } else {
                        LOGGER.error("Database query error", res.cause())
                        resultHandler.handle(Future.failedFuture(res.cause()))
                    }
                }
            }else {
                LOGGER.error("Database query error", car.cause());
                resultHandler.handle(Future.failedFuture(car.cause()))
            }
        }
        return this
    }

    override fun fetchPage(name: String, resultHandler: Handler<AsyncResult<JsonObject>>): WikiDatabaseService {
        dbClient.getConnection { car ->
            if (car.succeeded()) {
                val connection = car.result()
                connection.queryWithParams(sqlQueries[SqlQuery.GET_PAGE], JsonArray().add(name)) { fetch ->
                    connection.close()
                    if (fetch.succeeded()) {
                        val response = JsonObject()
                        val resultSet = fetch.result()
                        if (resultSet.numRows == 0) {
                            response.put("found", false)
                        } else {
                            response.put("found", true)
                            val row = resultSet.results[0]
                            response.put("id", row.getInteger(0))
                            response.put("rawContent", row.getString(1))
                        }
                        resultHandler.handle(Future.succeededFuture(response))
                    } else {
                        LOGGER.error("Database query error", fetch.cause())
                        resultHandler.handle(Future.failedFuture(fetch.cause()))
                    }
                }
            } else {
                LOGGER.error("Database query error", car.cause())
                resultHandler.handle(Future.failedFuture(car.cause()))
            }
        }
        return this
    }

    override fun createPage(title: String, markdown: String, resultHandler: Handler<AsyncResult<Void>>): WikiDatabaseService {
        dbClient.getConnection { car ->

            if (car.succeeded()) {
                val connection = car.result()
                val data = JsonArray().add(title).add(markdown)
                connection.updateWithParams(sqlQueries[SqlQuery.CREATE_PAGE], data) { res ->
                    connection.close()
                    if (res.succeeded()) {
                        resultHandler.handle(Future.succeededFuture())
                    } else {
                        LOGGER.error("Database query error", res.cause())
                        resultHandler.handle(Future.failedFuture(res.cause()))
                    }
                }
            } else {
                LOGGER.error("Database query error", car.cause())
                resultHandler.handle(Future.failedFuture(car.cause()))
            }
        }
        return this
    }

    override fun savePage(id: Int, markdown: String, resultHandler: Handler<AsyncResult<Void>>): WikiDatabaseService {
        dbClient.getConnection { car ->

            if (car.succeeded()) {
                val connection = car.result()
                val data = JsonArray().add(markdown).add(id)
                connection.updateWithParams(sqlQueries[SqlQuery.SAVE_PAGE], data) { res ->
                    connection.close()
                    if (res.succeeded()) {
                        resultHandler.handle(Future.succeededFuture())
                    } else {
                        LOGGER.error("Database query error", res.cause())
                        resultHandler.handle(Future.failedFuture(res.cause()))
                    }
                }
            } else {
                LOGGER.error("Database query error", car.cause())
                resultHandler.handle(Future.failedFuture(car.cause()))
            }
        }
        return this
    }

    override fun deletePage(id: Int, resultHandler: Handler<AsyncResult<Void>>): WikiDatabaseService {
        dbClient.getConnection { car ->
            if (car.succeeded()) {
                val connection = car.result()
                val data = JsonArray().add(id)
                connection.updateWithParams(sqlQueries[SqlQuery.DELETE_PAGE], data) { res ->
                    connection.close()
                    if (res.succeeded()) {
                        resultHandler.handle(Future.succeededFuture())
                    } else {
                        LOGGER.error("Database query error", res.cause())
                        resultHandler.handle(Future.failedFuture(res.cause()))
                    }
                }
            } else {
                LOGGER.error("Database query error", car.cause())
                resultHandler.handle(Future.failedFuture(car.cause()))
            }
        }
        return this
    }
}